home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Frameworks / Hsoi's App Shell 1.0a4 / HAS Other Source / WASTE 1.3a4 Distribution / WASTE 1.3a4 / Source / WELowLevelEditing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-04-11  |  26.5 KB  |  1,036 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WELowLevelEditing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Low-Level Editing Routines
  6.  *
  7.  *  Copyright (c) 1993-1997 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. pascal Boolean _WEIsWordRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  18. {
  19.     // _WEIsWordRange returns true if the specified range is a word range,
  20.     // i.e. if it would be possible to select it by double-clicking and (optionally) dragging.
  21.  
  22.     SInt32 wordStart, wordEnd;
  23.  
  24.     // determine if rangeStart is at the beginning of a word
  25.     WEFindWord(rangeStart, kLeadingEdge, &wordStart, &wordEnd, hWE);
  26.     if (rangeStart == wordStart)
  27.     {
  28.  
  29.         // determine if rangeEnd is at the end of a word
  30.         WEFindWord(rangeEnd, kTrailingEdge, &wordStart, &wordEnd, hWE);
  31.         return (rangeEnd == wordEnd);
  32.     }
  33.     return false;
  34. }
  35.  
  36. pascal Boolean _WEIsPunct(SInt32 offset, WEHandle hWE)
  37. {
  38.     SInt16 cType;
  39.  
  40.     cType = WECharType(offset, hWE);
  41.     if ((cType & smcTypeMask) == smCharPunct)
  42.     {
  43.         cType &= smcClassMask;
  44.         if ((cType == smPunctNormal) || (cType == smPunctBlank))
  45.         {
  46.             return true;
  47.         }
  48.     }
  49.     return false;
  50. }
  51.  
  52. pascal void _WEIntelligentCut(SInt32 *rangeStart, SInt32 *rangeEnd, WEHandle hWE)
  53. {
  54.  
  55.     // _WEIntelligentCut is called by other WASTE routines to determine the actual
  56.     // range to be deleted when weFIntCutAndPaste is enabled.
  57.     // On entry, rangeStart and rangeEnd specify the selection range visible to the user.
  58.     // On exit, rangeStart and rangeEnd specify the actual range to be removed.
  59.  
  60.     // do nothing if the intelligent cut-and-paste feature is disabled
  61.     if (!BTST((*hWE)->features, weFIntCutAndPaste))
  62.     {
  63.         return;
  64.     }
  65.  
  66.     // intelling cut-&-paste rules should be applied only to word ranges...
  67.     if (!_WEIsWordRange(*rangeStart, *rangeEnd, hWE))
  68.     {
  69.         return;
  70.     }
  71.  
  72.     // ...without punctuation characters at the beginning or end
  73.     if (_WEIsPunct(*rangeStart, hWE))
  74.     {
  75.         return;
  76.     }
  77.     if (_WEIsPunct(*rangeEnd - 1, hWE))
  78.     {
  79.         return;
  80.     }
  81.  
  82.     // if the character preceding the selection range is a space, discard it
  83.     if (WEGetChar(*rangeStart - 1, hWE) == kSpace)
  84.     {
  85.         (*rangeStart)--;
  86.     }
  87.     // else, if the character following the selection range is a space, discard it
  88.     else if (WEGetChar(*rangeEnd, hWE) == kSpace)
  89.     {
  90.         (*rangeEnd)++;
  91.     }
  92. }
  93.  
  94. pascal SInt16 _WEIntelligentPaste(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  95. {
  96.     SInt16 retval;
  97.  
  98.     // _WEIntelligentPaste is called by other WASTE routines to determine whether
  99.     // an additional space character should be added (before or after) after inserting
  100.     // new text (usually from the Clipboard or from a drag).
  101.  
  102.     retval = weDontAddSpaces;
  103.  
  104.     // do nothing unless the intelligent cut-and-paste feature is enabled
  105.     if (!BTST((*hWE)->features, weFIntCutAndPaste))
  106.     {
  107.         return retval;
  108.     }
  109.  
  110.     // extra spaces will be added only if the pasted text looks like a word range,
  111.     // without punctuation characters at the beginning or at the end
  112.     if (_WEIsPunct(rangeStart, hWE))
  113.     {
  114.         return retval;
  115.     }
  116.     if (_WEIsPunct(rangeEnd - 1, hWE))
  117.     {
  118.         return retval;
  119.     }
  120.  
  121.     // if the character on the left of the pasted text is a punctuation character
  122.     // and the character on the right isn't,  add a space on the right, and vice versa
  123.     if (_WEIsPunct(rangeStart - 1, hWE))
  124.     {
  125.         if (!_WEIsPunct(rangeEnd, hWE))
  126.         {
  127.             retval = weAddSpaceOnRightSide;
  128.         }
  129.     }
  130.     else if (_WEIsPunct(rangeEnd, hWE))
  131.     {
  132.         retval = weAddSpaceOnLeftSide;
  133.     }
  134.  
  135.     return retval;
  136. }
  137.  
  138. pascal OSErr _WEInsertRun(SInt32 runIndex, SInt32 offset, SInt32 styleIndex, WEPtr pWE)
  139. {
  140.  
  141.     // Insert a new entry in the style run array, at the specified runIndex position.
  142.     // The new entry consists of the pair <offset, styleIndex>.
  143.  
  144.     WERunArrayEntry entry;
  145.     OSErr err;
  146.  
  147.     // prepare the entry record to be inserted in the array
  148.     entry.runStart = offset;
  149.     entry.styleIndex = styleIndex;
  150.  
  151.     // do the insertion
  152.     if ((err = _WESplice((Handle) pWE->hRuns, &entry, sizeof(entry), (runIndex + 1) * sizeof(entry))) != noErr)
  153.     {
  154.         return err;
  155.     }
  156.  
  157.     // increment style run count
  158.     pWE->nRuns++;
  159.  
  160.     // increment the reference count field of the style table entry
  161.     // referenced by the newly inserted style run
  162.     (*pWE->hStyles)[styleIndex].refCount++;
  163.  
  164.     return noErr;
  165. }
  166.  
  167. pascal void _WERemoveRun(SInt32 runIndex, WEPtr pWE)
  168. {
  169.     WEStyleTableEntry *pStyle;
  170.  
  171.     // get a pointer to the style table entry referenced by the style run
  172.     pStyle = *pWE->hStyles + (*pWE->hRuns)[runIndex].styleIndex;
  173.  
  174.     // decrement the reference count field of the style table entry
  175.     // referenced by the style run to be removed
  176.     pStyle->refCount--;
  177.  
  178. #if WASTE_OBJECTS
  179.     // dispose of the embedded object (if any)
  180.     if (pStyle->info.runStyle.tsObject != nil)
  181.     {
  182.         _WEFreeObject(pStyle->info.runStyle.tsObject);
  183.     }
  184. #endif
  185.  
  186.     // remove a "slot" from the run array
  187.     _WESplice((Handle) pWE->hRuns, nil, - sizeof(WERunArrayEntry), runIndex * sizeof(WERunArrayEntry));
  188.  
  189.     // decrement style run count
  190.     pWE->nRuns--;
  191. }
  192.  
  193. pascal void _WEChangeRun(SInt32 runIndex, SInt32 newStyleIndex, Boolean keepOld, WEPtr pWE)
  194. {
  195.     // change the styleIndex field of the specified entry of the style run array
  196.  
  197.     SInt32 oldStyleIndex;
  198.     WEStyleTableEntry *oldStyle, *newStyle;
  199.  
  200.     // do the change
  201.     oldStyleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  202.     (*pWE->hRuns)[runIndex].styleIndex = newStyleIndex;
  203.  
  204.     // get pointers to old and new style table elements
  205.     oldStyle = *pWE->hStyles + oldStyleIndex;
  206.     newStyle = *pWE->hStyles + newStyleIndex;
  207.  
  208.     // increment the reference count field of the new style table entry
  209.     newStyle->refCount++;
  210.  
  211.     // decrement the reference count field of the old style table entry
  212.     oldStyle->refCount--;
  213.  
  214. #if WASTE_OBJECTS
  215.     // dispose of the embedded object (if any) unless it is again referenced in the new style
  216.     if (!keepOld)
  217.     {
  218.         WEObjectDescHandle oldObject = oldStyle->info.runStyle.tsObject;
  219.  
  220.         if ((oldObject != nil) && (oldObject != newStyle->info.runStyle.tsObject))
  221.         {
  222.             _WEFreeObject(oldObject);
  223.         }
  224.     }
  225. #endif
  226. }
  227.  
  228. pascal OSErr _WENewStyle(const WETextStyle *ts, SInt32 *styleIndex, WEPtr pWE)
  229. {
  230.     // given the specified WETextStyle record, find the corresponding entry
  231.     // in the style table (create a new entry if necessary), and return its index
  232.  
  233.     WEStyleTableEntry *pEntry;
  234.     WEStyleTableEntry entry;
  235.     SInt32 index;
  236.     SInt32 unusedIndex;
  237.     OSErr err;
  238.  
  239.     // see if the given style already exists in the style table
  240.     // while scanning the table, also remember the position of the first unused style, if any
  241.     unusedIndex = -1;
  242.     pEntry = *pWE->hStyles;
  243.     for ( index = 0 ; index < pWE->nStyles ; index++ )
  244.     {
  245.         // check for entries which aren't referenced and can be recycled
  246.         if (pEntry->refCount == 0)
  247.         {
  248.             unusedIndex = index;
  249.         }
  250.  
  251.         // perform a bitwise comparison between the current entry and the specified style
  252.         // (actually, we ignore metrics information)
  253.         else if (_WEBlockCmp(&pEntry->info.runStyle, ts, sizeof(WETextStyle)))
  254.         {
  255.             // found: style already present
  256.             *styleIndex = index;
  257.             return noErr;
  258.         }
  259.         pEntry++;
  260.     } // for
  261.  
  262.     // the specified style doesn't exist in the style table
  263.     // since this is a new entry, we have to calculate font metrics information
  264.     entry.info.runStyle = *ts;
  265.     _WEFillFontInfo(pWE->port, &entry.info);
  266.  
  267.     // see if we can recycle an unused entry
  268.     if (unusedIndex >= 0)
  269.     {
  270.         index = unusedIndex;
  271.         (*pWE->hStyles)[index].info = entry.info;
  272.     }
  273.     else
  274.     {
  275.         // no reusable entry: we have to append a new entry at the end of the table
  276.         entry.refCount = 0;
  277.         if ((err = _WESplice((Handle) pWE->hStyles, &entry, sizeof(entry), -1)) != noErr)
  278.         {
  279.             return err;
  280.         }
  281.  
  282.         // update style count in the WE record
  283.         pWE->nStyles++;
  284.     }
  285.  
  286.     // return the index to the new entry
  287.     *styleIndex = index;
  288.     return noErr;
  289. }
  290.  
  291. pascal OSErr _WERedraw(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  292. {
  293.     WEPtr pWE = *hWE;        // assume WE record is already locked
  294.     WELineRec *pLines;
  295.     SInt32 startLine, endLine;
  296.     SInt32 oldTextHeight, newTextHeight;
  297.     LongRect r;
  298.     Rect viewRect, updateRect;
  299.     RgnHandle saveClip;
  300.     GrafPtr savePort;
  301.     OSErr err;
  302. #if WASTE_REDRAW_SPEED
  303.     LongRect scrollRect;
  304.     RgnHandle updateRgn,
  305.               utilRgn;
  306. #endif
  307.  
  308.     // do nothing if recalculation has been inhibited
  309.     if (!BTST(pWE->features, weFInhibitRecal))
  310.     {
  311.         // hide the caret
  312. #if WASTE_REDRAW_SPEED
  313.         BCLR(pWE->flags, weFCaretVisible);
  314. #else
  315.         if (BTST(pWE->flags, weFCaretVisible))
  316.         {
  317.             _WEBlinkCaret(hWE);
  318.         }
  319. #endif
  320.  
  321.         // remember total text height
  322.         oldTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  323.  
  324.         // find line range affected by modification
  325.         startLine = WEOffsetToLine(rangeStart, hWE);
  326.         endLine = WEOffsetToLine(rangeEnd, hWE);
  327.  
  328.         // recalculate line breaks starting from startLine
  329.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) != noErr)
  330.         {
  331.             goto cleanup;
  332.         }
  333.  
  334.         // recalculate slops
  335.         _WERecalSlops(startLine, endLine, hWE);
  336.  
  337.         // do nothing if redrawing has been inhibited
  338.         if (!BTST(pWE->features, weFInhibitRedraw))
  339.         {
  340.  
  341.             // calculate new total text height
  342.             newTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  343.  
  344.             // calculate the rectangle to redraw (in long coordinates)
  345.             r.left = -SHRT_MAX;
  346.             r.right = SHRT_MAX;
  347.             pLines = *pWE->hLines;
  348.             r.top = pLines[startLine].lineOrigin;
  349.  
  350. #if WASTE_REDRAW_SPEED
  351.             // if total text height hasn't changed, it's enough to redraw lines up to endLine
  352.             // otherwise we must redraw all lines from startLine on
  353.  
  354.             if (endLine < pWE->nLines - 1)
  355.             {
  356.                  r.bottom = pLines[endLine + 1].lineOrigin;
  357.             }
  358.             else
  359.             {
  360.                 r.bottom = newTextHeight;
  361.             }
  362.             WEOffsetLongRect(&r, 0, pWE->destRect.top);
  363.  
  364.             if (newTextHeight == oldTextHeight)
  365.             {
  366.                 WELongRectToRect(&r, &updateRect);
  367.             }
  368.             else
  369.             {
  370.                 /*      Instead of scrolling the lines below the deleted text up by redrawing them,
  371.                  *      use scroll bits to move the displayed text up.
  372.                  */
  373.  
  374.                 scrollRect = pWE->viewRect;
  375.                 if (newTextHeight > oldTextHeight)
  376.                 {
  377.                     scrollRect.top = pLines[startLine + 1].lineOrigin + pWE->destRect.top;
  378.                 }
  379.                 else
  380.                 {
  381.                     scrollRect.top = pLines[startLine].lineOrigin + pWE->destRect.top;
  382.                 }
  383.                 WELongRectToRect(&scrollRect, &updateRect);
  384.                 updateRgn = NewRgn();
  385.                 ScrollRect(&updateRect, 0, newTextHeight - oldTextHeight, updateRgn);
  386.  
  387.                 //      Redraw the exposed region (caused by a scroll up)
  388.  
  389.                 WELongRectToRect(&r, &updateRect);
  390.                 utilRgn = NewRgn();
  391.                 RectRgn(utilRgn, &updateRect);
  392.                 DiffRgn(updateRgn, utilRgn, updateRgn);
  393.                 DisposeRgn(utilRgn);
  394.                 WEUpdate(updateRgn, hWE);
  395.                 DisposeRgn(updateRgn);
  396.             }
  397. #else
  398.             // if total text height hasn't changed, it's enough to redraw lines up to endLine
  399.             // otherwise we must redraw all lines from startLine on
  400.             if ((newTextHeight == oldTextHeight) && (endLine < pWE->nLines - 1))
  401.             {
  402.                 r.bottom = pLines[endLine + 1].lineOrigin;
  403.             }
  404.             else if (newTextHeight < oldTextHeight)
  405.             {
  406.                 r.bottom = oldTextHeight;
  407.             }
  408.             else
  409.             {
  410.                 r.bottom = newTextHeight;
  411.             }
  412.  
  413.             WEOffsetLongRect(&r, 0, pWE->destRect.top);
  414.  
  415.             // calculate the intersection between this rectangle and the view rectangle
  416.             WELongRectToRect(&r, &updateRect);
  417. #endif
  418.             WELongRectToRect(&pWE->viewRect, &viewRect);
  419.  
  420.             if (SectRect(&updateRect, &viewRect, &updateRect))
  421.             {
  422.                 // set up the port and the clip region
  423.                 GetPort(&savePort);
  424.                 SetPort(pWE->port);
  425.  
  426.                 // set the clip region to updateRect
  427.                 saveClip = NewRgn();
  428.                 GetClip(saveClip);
  429.                 ClipRect(&updateRect);
  430.  
  431.                 // we only really need to redraw the visible lines
  432.                 startLine = _WEPixelToLine(updateRect.top - pWE->destRect.top, hWE);
  433.                 endLine = _WEPixelToLine(updateRect.bottom - pWE->destRect.top - 1, hWE);
  434.  
  435.                 // redraw the lines (pass true in the doErase parameter)
  436.                 _WEDrawLines(startLine, endLine, true, hWE);
  437.  
  438.                 // erase the portion of the update rectangle below the last line (if any)
  439.                 pLines = *pWE->hLines;
  440.                 updateRect.top = pWE->destRect.top + pLines[endLine + 1].lineOrigin;
  441.                 if (updateRect.top < updateRect.bottom)
  442.                 {
  443.                     CallWEEraseProc(&updateRect, hWE, pWE->eraseHook);
  444.                 }
  445.  
  446.                 // restore the clip region
  447.                 SetClip(saveClip);
  448.                 DisposeRgn(saveClip);
  449.  
  450.                 // restore the port
  451.                 SetPort(savePort);
  452.  
  453.                 // redraw the caret or the selection range
  454.                 if (pWE->selStart < pWE->selEnd)
  455.                 {
  456.                     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  457.                 }
  458.                 else
  459.                 {
  460.                     _WEBlinkCaret(hWE);
  461.                 }
  462.             } // if SectRect
  463.  
  464.             // scroll the selection range into view
  465.             WESelView(hWE);
  466.  
  467.         } // if redraw not inhibited
  468.     } // if recal not inhibited
  469.  
  470.     // clear result code
  471.     err = noErr;
  472.  
  473. cleanup:
  474.     // return result code
  475.     return err;
  476. }
  477.  
  478. pascal OSErr WECalText(WEHandle hWE)
  479. {
  480.     Boolean saveWELock;
  481.     OSErr err;
  482.  
  483.     // lock WE record
  484.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  485.  
  486. #if WASTE_WECALTEXT_DOES_REDRAW
  487.  
  488.     // recalculate line breaks & slops and redraw the text
  489.     err = _WERedraw(0, LONG_MAX, hWE);
  490.  
  491. #else
  492.  
  493.     {
  494.         SInt32 startLine = 0;
  495.         SInt32 endLine = LONG_MAX;
  496.  
  497.         // recalculate line breaks & slops without redrawing anything
  498.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) == noErr)
  499.         {
  500.             _WERecalSlops(startLine, endLine, hWE);
  501.         }
  502.     }
  503.  
  504. #endif
  505.  
  506.     // unlock the WE record
  507.     _WESetHandleLock((Handle) hWE, saveWELock);
  508.  
  509.     // return result code
  510.     return err;
  511. }
  512.  
  513. pascal OSErr _WESetStyleRange(SInt32 rangeStart, SInt32 rangeEnd, WEStyleMode mode, const WETextStyle *ts, WEHandle hWE)
  514. {
  515.     // alter the style attributes of the specified text range according to ts and mode
  516.  
  517.     WEPtr pWE = *hWE;                    // assume WE record is already locked
  518.     WERunArrayHandle hRuns = pWE->hRuns;
  519.     SInt32 offset;
  520.     SInt32 runIndex;
  521.     SInt32 oldStyleIndex, newStyleIndex;
  522.     WERunInfo runInfo;
  523.     Style continuousStyles;
  524.     OSErr err;
  525.  
  526.     WEASSERT(((rangeStart < rangeEnd) || ((rangeStart == 0) && (rangeEnd == 0) && (pWE->textLength == 0))), "\pBad style range");
  527.  
  528.     // if mode contains weDoToggleFace, we need to determine which Quickdraw styles
  529.     // are continuous over the specified text range: those styles must be turned off
  530.     if (BTST(mode, kModeToggleFace))
  531.     {
  532.         WEStyleMode temp = weDoFace;
  533.         _WEContinuousStyleRange(rangeStart, rangeEnd, &temp, &runInfo.runAttrs.runStyle, hWE);
  534.         continuousStyles = runInfo.runAttrs.runStyle.tsFace;
  535.     }
  536.     else
  537.     {
  538.         continuousStyles = normal;
  539.     }
  540.  
  541.     // find the index to the first style run in the specified range
  542.     offset = rangeStart;
  543.     runIndex = WEOffsetToRun(offset, hWE);
  544.  
  545.     // run thru all the style runs that encompass the selection range
  546.     do
  547.     {
  548.         // find style index for this run and retrieve corresponding style attributes
  549.         oldStyleIndex = (*hRuns)[runIndex].styleIndex;
  550.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  551.  
  552.         // _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run:
  553.         // correct this anomaly (which is useful for other purposes, anyway)
  554.         if (runInfo.runEnd > pWE->textLength)
  555.         {
  556.             runInfo.runEnd = pWE->textLength;
  557.         }
  558.  
  559.         // apply changes to existing style attributes as requested
  560.         _WECopyStyle(ts, &runInfo.runAttrs.runStyle, continuousStyles, mode);
  561.  
  562.         // the high bit of tsFlags must be set if the font
  563.         // belongs to a right-to-left script system
  564.         if (BTST(pWE->flags, weFBidirectional))
  565.         {
  566.             if (GetScriptVariable(FontToScript(runInfo.runAttrs.runStyle.tsFont), smScriptRight) != 0)
  567.             {
  568.                 BSET(runInfo.runAttrs.runStyle.tsFlags, tsRightToLeft);
  569.             }
  570.         }
  571.  
  572.         // get a style index for the new text style
  573.         if ((err = _WENewStyle(&runInfo.runAttrs.runStyle, &newStyleIndex, pWE)) != noErr)
  574.         {
  575.             goto cleanup;
  576.         }
  577.  
  578.         // if offset falls on a style boundary and this style run has become identical
  579.         // to the previous one, merge the two runs together
  580.         if ((offset == runInfo.runStart) && (runIndex > 0) &&
  581.             ((*hRuns)[runIndex - 1].styleIndex == newStyleIndex))
  582.         {
  583.             _WERemoveRun(runIndex, pWE);
  584.             runIndex--;
  585.         }
  586.  
  587.         // style index changed?
  588.         if (oldStyleIndex != newStyleIndex)
  589.         {
  590.             // if offset is in the middle of a style run, insert a new style run in the run array
  591.             if (offset > runInfo.runStart)
  592.             {
  593.                 if ((err = _WEInsertRun(runIndex, offset, newStyleIndex, pWE)) != noErr)
  594.                 {
  595.                     goto cleanup;
  596.                 }
  597.                 runIndex++;
  598.             }
  599.             else
  600.             {
  601.                 // otherwise just change the styleIndex field of the current style run entry
  602.                 _WEChangeRun(runIndex, newStyleIndex, (rangeEnd < runInfo.runEnd), pWE);
  603.             }
  604.  
  605.             // if specified range ends in the middle of a style run, insert yet another entry
  606.             if (rangeEnd < runInfo.runEnd)
  607.             {
  608.                 if ((err = _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE)) != noErr)
  609.                 {
  610.                     goto cleanup;
  611.                 }
  612.             }
  613.         } // if oldStyle != newStyle
  614.  
  615.         // go to next style run
  616.         runIndex++;
  617.         offset = runInfo.runEnd;
  618.  
  619.     } while (offset < rangeEnd);
  620.  
  621.     // if the last style run ends exactly at the end of the specified range,
  622.     // see if we can merge it with the following style run
  623.     if ((offset == rangeEnd) && (runIndex < pWE->nRuns) &&
  624.         ((*hRuns)[runIndex].styleIndex == newStyleIndex))
  625.     {
  626.         _WERemoveRun(runIndex, pWE);
  627.     }
  628.  
  629.     // clear result code
  630.     err = noErr;
  631.  
  632. cleanup:
  633.     // return result code
  634.     return err;
  635. }
  636.  
  637. pascal OSErr _WEApplyStyleScrap(SInt32 rangeStart, SInt32 rangeEnd, StScrpHandle styleScrap, WEHandle hWE)
  638. {
  639.     // apply the given style scrap to the specified text range
  640.  
  641.     WEPtr pWE = *hWE;    // assume WE record is already locked
  642.     TEStyleScrapElement *pEntry;
  643.     Size styleScrapSize;
  644.     SInt32 runStart, runEnd;
  645.     SInt32 entryCount, index;
  646.     WETextStyle ts;
  647.     OSErr err;
  648.  
  649.     // sanity check: make sure the style scrap handle is a reasonable size
  650.     err = weCorruptDataErr;
  651.     styleScrapSize = GetHandleSize((Handle) styleScrap) - sizeof(SInt16);
  652.     if (styleScrapSize < 0)
  653.     {
  654.         goto cleanup;        // too short: header is missing
  655.     }
  656.  
  657.     // calculate entry count based on handle size
  658.     entryCount = styleScrapSize / sizeof(ScrpSTElement);
  659.     if (styleScrapSize != entryCount * sizeof(ScrpSTElement))
  660.     {
  661.         goto cleanup;        // not an integral number of entries
  662.     }
  663.  
  664.     // the calculated entry count must match scrpNStyles,
  665.     // unless it's larger than 32,766
  666.     if (entryCount < SHRT_MAX)
  667.     {
  668.         if (entryCount != (*styleScrap)->scrpNStyles)
  669.         {
  670.             goto cleanup;    // invalid entry count
  671.         }
  672.     }
  673.  
  674.     // loop through each entry of the style scrap
  675.     for ( index = 0; index < entryCount; index++ )
  676.     {
  677.         // get a pointer to the current scrap entry
  678.         pEntry = (TEStyleScrapElement *) ((*styleScrap)->scrpStyleTab + index);
  679.  
  680.         // calculate text run to which this entry is to be applied
  681.         runStart = rangeStart + pEntry->scrpStartChar;
  682.         if (index < entryCount - 1)
  683.         {
  684.             runEnd = rangeStart + pEntry[1].scrpStartChar;
  685.         }
  686.         else
  687.         {
  688.             runEnd = rangeEnd;
  689.         }
  690.  
  691.         // perform some range checking
  692.         if (runEnd > rangeEnd)
  693.         {
  694.             runEnd = rangeEnd;
  695.         }
  696.         if (runStart >= runEnd)
  697.         {
  698.             continue;
  699.         }
  700.  
  701.         // copy style to a local variable in case memory moves
  702.         * (TextStyle *) &ts = pEntry->scrpTEAttrs.runTEStyle;
  703.  
  704.         // apply the specified style to the range
  705.         if ((err = _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, &ts, hWE)) != noErr)
  706.         {
  707.             goto cleanup;
  708.         }
  709.     }
  710.  
  711.     // clear result code
  712.     err = noErr;
  713.  
  714. cleanup:
  715.     // return result code
  716.     return err;
  717. }
  718.  
  719. #if WASTE_OBJECTS
  720.  
  721. pascal OSErr _WEApplySoup(SInt32 offset, Handle hSoup, WEHandle hWE)
  722. {
  723.     WESoup soup;
  724.     Ptr pSoup, pSoupEnd;
  725.     WETextStyle ts;
  726.     Handle hObjectData;
  727.     SInt32 objectOffset;
  728.     Boolean saveWELock;
  729.     OSErr err;
  730.  
  731.     BLOCK_CLR(ts);
  732.     hObjectData = nil;
  733.  
  734.     // lock the WE record
  735.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  736.  
  737.     // lock the soup in high heap
  738.     HLockHi(hSoup);
  739.     pSoup = *hSoup;
  740.     pSoupEnd = pSoup + GetHandleSize(hSoup);
  741.  
  742.     // loop through each object descriptor in the soup
  743.     while (pSoup < pSoupEnd)
  744.     {
  745.         // Object descriptors may be aligned to odd addresses (duh!)
  746.         // this isn't a problem on 68020+ and PPC CPUs, but causes
  747.         // a fatal address error on the 68000.  To avoid this, we
  748.         // copy the descriptor to the stack with BlockMoveData()
  749.         // before trying to access its fields.
  750.         BlockMoveData(pSoup, &soup, sizeof(soup));
  751.  
  752.         // if soupDataSize is negative, this soup is a special type that we won't handle here
  753.         if (soup.soupDataSize < 0)
  754.         {
  755.             continue;
  756.         }
  757.  
  758.         // create a new relocatable block the hold the object data
  759.         if ((err = _WEAllocate(soup.soupDataSize, kAllocTemp, &hObjectData)) != noErr)
  760.         {
  761.             goto cleanup;
  762.         }
  763.  
  764.         // copy the object data to this block
  765.         BlockMoveData(pSoup + sizeof(soup), *hObjectData, soup.soupDataSize);
  766.  
  767.         // create a new object out of the tagged data
  768.         if ((err = _WENewObject(soup.soupType, hObjectData, hWE, &ts.tsObject)) != noErr)
  769.         {
  770.             goto cleanup;
  771.         }
  772.  
  773.         // if there was no new handler for this object, use the object size stored in the soup
  774.         if ((*ts.tsObject)->objectTable == nil)
  775.         {
  776.             (*ts.tsObject)->objectSize = soup.soupSize;
  777.         }
  778.  
  779.         // record a reference to the object descriptor in the style table
  780.         objectOffset = soup.soupOffset + offset;
  781.         err = _WESetStyleRange(objectOffset, objectOffset + 1, weDoObject, &ts, hWE);
  782.         hObjectData = nil;
  783.         ts.tsObject = nil;
  784.         if (err != noErr)
  785.         {
  786.             goto cleanup;
  787.         }
  788.  
  789.         // advance soup pointer
  790.         pSoup += sizeof(soup) + soup.soupDataSize;
  791.  
  792.     } // while
  793.  
  794.     // clear result code
  795.     err = noErr;
  796.  
  797. cleanup:
  798.     // clean up
  799.     HUnlock(hSoup);
  800.     _WEForgetHandle((Handle *) &ts.tsObject);
  801.     _WEForgetHandle(&hObjectData);
  802.  
  803.     // unlock the WE record
  804.     _WESetHandleLock((Handle) hWE, saveWELock);
  805.  
  806.     // return result code
  807.     return err;
  808. }
  809.  
  810. #endif    // WASTE_OBJECTS
  811.  
  812. pascal void _WEBumpRunStart(SInt32 runIndex, SInt32 deltaRunStart, WEPtr pWE)
  813. {
  814.     // add deltaLineStart to the lineStart field of all line records
  815.     // starting from lineIndex
  816.  
  817.     WERunArrayEntry *pRun = *pWE->hRuns + runIndex;
  818.     SInt32 nRuns = pWE->nRuns;
  819.  
  820.     // loop through the style run array adjusting the runStart fields
  821.     for ( ; runIndex <= nRuns; runIndex++ )
  822.     {
  823.         pRun->runStart += deltaRunStart;
  824.         pRun++;
  825.     }
  826. }
  827.  
  828. pascal void _WERemoveRunRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  829. {
  830.     // the range of text between rangeStart and rangeEnd is being deleted
  831.     // update the style run array (and the style table) accordingly
  832.  
  833.     WEPtr pWE = *hWE;    // assume WE record is already locked
  834.     WERunArrayEntry *pRun;
  835.     SInt32 startRun, endRun;
  836.  
  837.     // find the index to the first and last style runs in the specified range
  838.     startRun = WEOffsetToRun(rangeStart, hWE);
  839.     endRun = WEOffsetToRun(rangeEnd, hWE) - 1;
  840.  
  841.     // remove all style runs between startRun and endRun
  842.     for ( ; endRun > startRun; endRun-- )
  843.     {
  844.         _WERemoveRun(endRun, pWE);
  845.     }
  846.  
  847.     // move back all subsequent style runs
  848.     _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  849.  
  850.     if ((endRun == startRun) && (endRun < pWE->nRuns - 1))
  851.     {
  852.         pRun = *pWE->hRuns + endRun;
  853.         pRun[1].runStart = rangeStart;
  854.     }
  855.  
  856.     // remove the first style run if is has become zero length
  857.     pRun = *pWE->hRuns + startRun;
  858.     if (pRun[0].runStart == pRun[1].runStart)
  859.     {
  860.         _WERemoveRun(startRun, pWE);
  861.         startRun--;
  862.     }
  863.  
  864.     // merge the first and last runs if they have the same style index
  865.     if (startRun >= 0)
  866.     {
  867.         pRun = *pWE->hRuns + startRun;
  868.         if (pRun[0].styleIndex == pRun[1].styleIndex)
  869.         {
  870.             _WERemoveRun(startRun + 1, pWE);
  871.         }
  872.     }
  873. }
  874.  
  875. pascal void _WEBumpLineStart(SInt32 lineIndex, SInt32 deltaLineStart, WEPtr pWE)
  876. {
  877.     // add deltaLineStart to the lineStart field of all line records
  878.     // starting from lineIndex
  879.  
  880.     WELineRec *pLine = *pWE->hLines + lineIndex;
  881.     SInt32 nLines = pWE->nLines;
  882.  
  883.     // loop through the line array adjusting the lineStart fields
  884.     for ( ; lineIndex <= nLines; lineIndex++ )
  885.     {
  886.         pLine->lineStart += deltaLineStart;
  887.         pLine++;
  888.     }
  889. }
  890.  
  891. pascal void _WERemoveLineRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  892. {
  893.     // the range of text between rangeStart and rangeEnd is being deleted
  894.     // update the line array accordingly
  895.  
  896.     WEPtr pWE = *hWE;    // assume WE record is already locked
  897.     SInt32 startLine, nLines;
  898.  
  899.     // remove all line records between rangeStart and rangeEnd
  900.     startLine = WEOffsetToLine(rangeStart, hWE) + 1;
  901.     nLines = WEOffsetToLine(rangeEnd, hWE) - startLine + 1;
  902.  
  903.     _WESplice((Handle) pWE->hLines, nil, - (nLines * sizeof(WELineRec)), startLine * sizeof(WELineRec));
  904.     pWE->nLines -= nLines;
  905.  
  906.     // update the lineStart field of all the line records that follow
  907.     _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  908. }
  909.  
  910. pascal OSErr _WEDeleteRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  911. {
  912.     // used internally to delete a text range
  913.     WEPtr pWE = *hWE;    // assume WE record is already locked
  914.     WERunInfo runInfo;
  915.     SInt32 delta;
  916.     OSErr err = noErr;
  917.  
  918.     if (rangeEnd > pWE->textLength)
  919.     {
  920.         rangeEnd = pWE->textLength;
  921.     }
  922.  
  923.     // do nothing if the specified range is empty
  924.     if ((delta = (rangeStart - rangeEnd)) == 0)
  925.     {
  926.         goto cleanup;
  927.     }
  928.  
  929.     // save the first style in the specified range in nullStyle
  930.     WEGetRunInfo(rangeStart, &runInfo, hWE);
  931.     pWE->nullStyle = runInfo.runAttrs;
  932.     BSET(pWE->flags, weFUseNullStyle);
  933.  
  934. #if WASTE_OBJECTS
  935.     // special case: if we're deleting up to the end of the text, see whether
  936.     // there's an embedded object at the very end and remove it
  937.     if (rangeEnd == pWE->textLength)
  938.     {
  939.         WEGetRunInfo(rangeEnd - 1, &runInfo, hWE);
  940.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  941.         {
  942.             runInfo.runAttrs.runStyle.tsObject = nil;
  943.             if ((err = _WESetStyleRange(rangeEnd - 1, rangeEnd, weDoObject, &runInfo.runAttrs.runStyle, hWE)) != noErr)
  944.             {
  945.                 goto cleanup;
  946.             }
  947.         }
  948.     }
  949. #endif
  950.  
  951.     // remove all line records between rangeStart and rangeEnd
  952.     _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  953.  
  954.     // remove all style runs between rangeStart and rangeEnd
  955.     _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  956.  
  957.     // remove the text
  958.     if ((err = _WESplice(pWE->hText, nil, delta, rangeStart)) != noErr)
  959.     {
  960.         goto cleanup;
  961.     }
  962.  
  963.     // update textLength field
  964.     pWE->textLength += delta;
  965.  
  966.     // we modified the text, so the anchor range (if any) is no longer valid
  967.     pWE->clickCount = 0;
  968.  
  969.     // call the flux callback, if any
  970.     if (pWE->fluxProc != nil)
  971.     {
  972.         CallWEFluxProc(rangeStart, delta, hWE, pWE->fluxProc);
  973.     }
  974.  
  975. cleanup:
  976.     // return result code
  977.     return err;
  978. }
  979.  
  980. pascal OSErr _WEInsertText(SInt32 offset, Ptr textPtr, SInt32 textLength, WEHandle hWE)
  981. {
  982.     WEPtr pWE = *hWE;    // assume WE record is already locked
  983.     WEStyleMode mode;
  984.     OSErr err = noErr;
  985.  
  986.     // do nothing if textLength is zero or negative
  987.     if (textLength <= 0)
  988.     {
  989.         goto cleanup;
  990.     }
  991.  
  992.     // insert the text
  993.     if ((err = _WESplice(pWE->hText, textPtr, textLength, offset)) != noErr)
  994.     {
  995.         goto cleanup;
  996.     }
  997.  
  998.     // update the lineStart fields of all lines following the insertion point
  999.     _WEBumpLineStart(WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  1000.  
  1001.     // update the runStart fields of all style runs following the insertion point
  1002.     _WEBumpRunStart(WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  1003.  
  1004.     // update the textLength field
  1005.     pWE->textLength += textLength;
  1006.  
  1007.     // we modified the text, so the anchor range (if any) is no longer valid
  1008.     pWE->clickCount = 0;
  1009.  
  1010.     // make sure the newly inserted text doesn't reference any embedded object
  1011. #if WASTE_OBJECTS
  1012.     pWE->nullStyle.runStyle.tsObject = nil;
  1013. #endif
  1014.     mode = weDoObject;
  1015.  
  1016.     // if there is a valid null style, apply it to the newly inserted text
  1017.     if (BTST(pWE->flags, weFUseNullStyle))
  1018.     {
  1019.         mode += (weDoAll + weDoReplaceFace);
  1020.     }
  1021.  
  1022.     if ((err = _WESetStyleRange(offset, offset + textLength, mode, &pWE->nullStyle.runStyle, hWE)) != noErr)
  1023.     {
  1024.         goto cleanup;
  1025.     }
  1026.  
  1027.     // call the flux callback, if any
  1028.     if (pWE->fluxProc != nil)
  1029.     {
  1030.         CallWEFluxProc(offset, textLength, hWE, pWE->fluxProc);
  1031.     }
  1032.  
  1033. cleanup:
  1034.     return err;
  1035. }
  1036.